<template>
  <section id="demos" class="invisible">
    <div id="liveView" class="videoView">
      <v-btn id="webcamButton">GESTURE</v-btn>
      <div style="position: relative">
        <video id="webcam" autoplay playsinline></video>
        <canvas
          class="output_canvas"
          id="output_canvas"
          width="1280"
          height="720"
          style="position: absolute; left: 0px; top: 0px"
        ></canvas>
      </div>
    </div>
  </section>
</template>

<script setup lang="ts" defer="true">
import { reflectAxis } from '../libs/helpers';
import {
  GestureRecognizer,
  HandLandmarker,
  FilesetResolver,
  DrawingUtils,
  HandLandmarkerResult,
} from '@mediapipe/tasks-vision';
import { onMounted } from 'vue';
import GestureEvent from '~/game/models/GestureEvent';

declare type RunningMode = 'IMAGE' | 'VIDEO';

const props = defineProps<{
  handleGestureMove: (e: MouseEvent | GestureEvent | TouchEvent) => void;
}>();

onMounted(() => {
  const demosSection = document.getElementById('demos');
  let handLandmarker: HandLandmarker | undefined = undefined;
  let enableWebcamButton: HTMLButtonElement;
  let runningMode: RunningMode = 'IMAGE';
  let webcamRunning = false;

  const videoHeight = '360px';
  const videoWidth = '480px';
  const createHandLandmarker = async () => {
    const vision = await FilesetResolver.forVisionTasks(
      'https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@0.10.0/wasm',
    );
    handLandmarker = await HandLandmarker.createFromOptions(vision, {
      baseOptions: {
        modelAssetPath:
          'https://storage.googleapis.com/mediapipe-models/hand_landmarker/hand_landmarker/float16/1/hand_landmarker.task',
        delegate: 'GPU',
      },
      runningMode,
      numHands: 2,
    });
    demosSection?.classList.remove('invisible');
  };
  createHandLandmarker();

  const video = document.getElementById('webcam') as HTMLVideoElement;
  const canvasElement = document.getElementById(
    'output_canvas',
  ) as HTMLCanvasElement;
  const canvasCtx = canvasElement.getContext('2d') as CanvasRenderingContext2D;

  // Check if webcam access is supported.
  const hasGetUserMedia = () => !!navigator.mediaDevices?.getUserMedia;

  // If webcam supported, add event listener to button for when user
  // wants to activate it.
  if (hasGetUserMedia()) {
    enableWebcamButton = document.getElementById(
      'webcamButton',
    ) as HTMLButtonElement;
    enableWebcamButton.addEventListener('click', enableCam);
  } else {
    console.warn('getUserMedia() is not supported by your browser');
  }

  // Enable the live webcam view and start detection.
  function enableCam() {
    if (!handLandmarker) {
      alert('Please wait for handlandMarker to load');
      return;
    }

    if (webcamRunning) {
      webcamRunning = false;
      enableWebcamButton.innerText = 'GESTURE';
    } else {
      webcamRunning = true;
      enableWebcamButton.innerText = 'DISABLE';
    }

    // getUsermedia parameters.
    const constraints = {
      video: true,
    };

    // Activate the webcam stream.
    navigator.mediaDevices.getUserMedia(constraints).then(function (stream) {
      video.srcObject = stream;
      video.addEventListener('loadeddata', predictWebcam);
    });
  }

  let lastVideoTime = -1;
  let results: HandLandmarkerResult | undefined = undefined;
  console.log(video);

  async function predictWebcam() {
    const webcamElement = document.getElementById('webcam') as HTMLVideoElement;

    // Now let's start detecting the stream.
    if (runningMode === 'IMAGE') {
      runningMode = 'VIDEO';
      await handLandmarker?.setOptions({ runningMode: 'VIDEO' });
    }

    const startTimeMs = performance.now();
    if (lastVideoTime !== video.currentTime) {
      lastVideoTime = video.currentTime;
      results = handLandmarker?.detectForVideo(video, startTimeMs);
    }
    if (results?.landmarks?.[0]?.[8]?.x && results?.landmarks?.[0]?.[8]?.y) {
      props.handleGestureMove(
        reflectAxis({
          offsetX: results?.landmarks?.[0]?.[8]?.x,
          offsetY: results?.landmarks?.[0]?.[8]?.y,
        }),
      );
    }

    canvasCtx?.save();
    canvasCtx?.clearRect(0, 0, canvasElement.width, canvasElement.height);
    const drawingUtils = new DrawingUtils(canvasCtx);

    canvasElement.style.height = videoHeight;
    webcamElement.style.height = videoHeight;
    canvasElement.style.width = videoWidth;
    webcamElement.style.width = videoWidth;

    if (results?.landmarks) {
      for (const landmarks of results.landmarks) {
        drawingUtils.drawConnectors(
          landmarks,
          GestureRecognizer.HAND_CONNECTIONS,
          {
            color: '#00FF00',
            lineWidth: 5,
          },
        );
        drawingUtils.drawLandmarks(landmarks, {
          color: '#FF0000',
          lineWidth: 2,
        });
      }
    }
    canvasCtx.restore();

    // Call this function again to keep predicting when the browser is ready.
    if (webcamRunning) {
      window.requestAnimationFrame(predictWebcam);
    }
  }
});
</script>

<style scoped>
h1 {
  color: #007f8b;
}

h2 {
  clear: both;
}

video {
  display: block;
  clear: both;
  transform: rotateY(180deg);
  -webkit-transform: rotateY(180deg);
  -moz-transform: rotateY(180deg);
  height: 280px;
}

section {
  opacity: 1;
  transition: opacity 500ms ease-in-out;
}

.removed {
  display: none;
}

.invisible {
  opacity: 0.2;
}

.detectOnClick {
  position: relative;
  float: left;
  cursor: pointer;
  margin: 2% 1%;
  width: 48%;
}
.videoView {
  position: absolute;
  float: left;
  cursor: pointer;
  margin: 2% 1%;
  width: 48%;
  min-height: 500px;
}

.videoView p,
.detectOnClick p {
  z-index: 2;
  margin: 0;
  border: 1px dashed rgba(255, 255, 255, 0.7);
  background-color: #007f8b;
  padding-top: 5px;
  padding-bottom: 5px;
  color: #fff;
}

.highlighter {
  position: absolute;
  z-index: 1;
  border: 1px dashed #fff;
  background: rgba(0, 255, 0, 0.25);
}

.canvas {
  position: absolute;
  z-index: 1;
  pointer-events: none;
}

.output_canvas {
  transform: rotateY(180deg);
  -webkit-transform: rotateY(180deg);
  -moz-transform: rotateY(180deg);
}

.detectOnClick {
  z-index: 0;
  font-size: calc(8px + 1.2vw);
}

.detectOnClick img {
  width: 45vw;
}
.output {
  display: none;
  width: 100%;
  font-size: calc(8px + 1.2vw);
}
</style>
